﻿
using DoNetDrive.Core;
using DoNetDrive.Core.Connector;
using DoNetDrive.Core.Connector.TCPClient;
using DotNetty.Buffers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Text;
using System.Threading.Tasks;

namespace DoNetDrive.AspWebSocke.WebSocketClient
{
    public class WebSocketClientConnector : AbstractConnector
    {
        protected int DefaultReadDataBufferSize { get; set; } = 2048;
        protected int _ConnectTimeoutMSEL;
        protected int _ReconnectMax;
        protected int _ReconnectCount;

        WebSocketClientDetail ClientDetail;
        private WebSocket Client;
        public WebSocketClientConnector(WebSocketClientDetail detail)
        {
            ClientDetail = detail;
            SetConnectOption(detail);
            _ReconnectCount = 0;
            _ReconnectMax = 3;
            _ConnectorDetail = detail;
        }

        private void SetConnectOption(WebSocketClientDetail detail)
        {
            _ConnectTimeoutMSEL = detail.Timeout;
            _ReconnectMax = detail.RestartCount;
            if (_ConnectTimeoutMSEL > TCPClientFactory.CONNECT_TIMEOUT_MILLIS_MAX)
                _ConnectTimeoutMSEL = TCPClientFactory.CONNECT_TIMEOUT_MILLIS_MAX;
            else if (_ConnectTimeoutMSEL < TCPClientFactory.CONNECT_TIMEOUT_MILLIS_MIN)
                _ConnectTimeoutMSEL = TCPClientFactory.CONNECT_TIMEOUT_MILLIS_MIN;
            if (_ReconnectMax > TCPClientFactory.CONNECT_RECONNECT_MAX)
                _ReconnectMax = TCPClientFactory.CONNECT_RECONNECT_MAX;
            else if (_ReconnectMax < 0)
                _ReconnectMax = 0;
        }
        public override async Task CloseAsync()
        {
            if (_isRelease || (!_IsActivity))
                return;
            _Status = ConnectorStatus.Invalid;
            try
            {
                if (Client != null && Client.State == WebSocketState.Open)
                    await Client.CloseAsync(WebSocketCloseStatus.Empty, "", CancellationToken.None);
            }
            catch (Exception ex)
            {
                _ConnectorDetail.SetError(ex);
            }
            Client = null;
            UpdateActivityTime();
            FireConnectorClosedEvent(_ConnectorDetail);
            SetInvalid();
            _IsForcibly = false;
            ClientDetail.Connected = false;
        }

        public override async Task ConnectAsync()
        {

            if (CheckIsInvalid()) return;
            if (IsActivity()) return;
            _Status = WebSocketClientConnectorStatus.Connecting;
            UpdateActivityTime();
            var client = new ClientWebSocket();
            if (ClientDetail.Headers != null)
            {
                foreach (var item in ClientDetail.Headers)
                {
                    client.Options.SetRequestHeader(item.Key, item.Value);
                }
            }
            await client.ConnectAsync(ClientDetail.Uri, CancellationToken.None);
            Client = client;
            _Status = WebSocketClientConnectorStatus.Connected;
            //  _IsForcibly = true;
            _IsActivity = true;
            ClientDetail.Connected = true;
            Task.Run(ReceiveAsync);
            FireConnectorConnectedEvent(ClientDetail);
            CheckCommandList();
            //    }
            //catch (Exception ex)
            //{
            //    //  _Status = WebSocketClientConnectorStatus.Closed;
            //    Console.WriteLine("连接发生错误:" + ex.Message);
            //    _IsActivity = false;
            //    ConnectFail(ex);
            //}
        }

        private async Task ReceiveAsync()
        {
            try
            {
                var buffer = new byte[DefaultReadDataBufferSize];
                var utf8 = System.Text.Encoding.UTF8;
                IByteBuffer NettyBuf = null;
                WebSocketReceiveResult result;
                int len = 0;
                var defultLen = 1024 * 200;
                while (true)
                {
                    result = await Client.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
                    if (result.CloseStatus.HasValue) break;

                    if (NettyBuf == null)
                    {
                        NettyBuf = GetByteBufAllocator().Buffer(defultLen);
                    }
                    NettyBuf.WriteBytes(buffer, 0, result.Count);
                    len += result.Count;
                    if (result.EndOfMessage)
                    {
                        NettyBuf.SetWriterIndex(len);
                        ReadByteBuffer(NettyBuf);
                        NettyBuf.Clear();
                        len = 0;
                    }
                }
            }
            catch (Exception ex)
            {
                if (ex is SocketException sEx)
                {
                    if (sEx.ErrorCode == 995)
                    {
                        return;
                    }
                    if (sEx.ErrorCode == 10054)
                    {
                        _ConnectorDetail.SetError(ex);
                        FireConnectorErrorEvent(_ConnectorDetail);
                        SetInvalid();
                    }
                }
                else
                {
                    ConnectFail(ex);
                }
                //Console.WriteLine("连接发生错误:" + ex.Message);
                //_IsActivity = false;
                //_Status = WebSocketClientConnectorStatus.Closed;
                //FireClientOffline(this);
            }
        }
        public override string GetConnectorType()
        {
            return ConnectorType.WebSocketClient;
        }

        public override IPDetail LocalAddress()
        {
            return default(IPDetail);
        }

        public override IPDetail RemoteAddress()
        {
            return default(IPDetail);
        }

        protected override INConnectorStatus GetInitializationStatus()
        {
            return WebSocketClientConnectorStatus.Closed;
        }

        protected override void Release0()
        {

        }
        public void ConnectingNext(Task connTask)
        {
            if (CheckIsInvalid()) return;
            if (!_Status.Status().Equals("Connecting")) return;
            if (Client == null) return;
            if (connTask.IsFaulted || connTask.IsCanceled)
            {
                ConnectFail(connTask.Exception);
            }
        }
        public virtual void ConnectFail(Exception ex)
        {
            if (ex is ObjectDisposedException ||
                _Status.Status().Equals("Invalid"))
            {
                return;
            }
            _IsActivity = false;
            if (_ReconnectCount > _ReconnectMax)
            {
                if (_IsForcibly)
                {
                    _ReconnectCount = 0;
                    _Status = WebSocketClientConnectorStatus.Closed;
                }
                else
                {
                    _ConnectorDetail.SetError(new Exception("已达到最大重试次数", ex));
                    FireConnectorErrorEvent(_ConnectorDetail);
                    SetInvalid();
                    ClientDetail.Connected = false;
                }
            }
            else
            {
                _Status = WebSocketClientConnectorStatus.Closed;
            }
            _ReconnectCount++;
        }
        protected override async Task WriteByteBuf0(IByteBuffer buf)
        {
            await WriteBinary(buf);
        }
        public async Task WriteBinary(IByteBuffer buf)
        {
            await WriteByteBuf(buf, WebSocketMessageType.Binary);
        }
        public async Task WriteText(IByteBuffer buffer)
        {
            await WriteByteBuf(buffer, WebSocketMessageType.Text);
        }
        private async Task WriteByteBuf(IByteBuffer buf, WebSocketMessageType messageType)
        {
            if (CheckIsInvalid())
            {
                await Task.FromException(new Exception($"connect is {_Status.Status}"));
            }
            if (Client != null && Client.State == WebSocketState.Open)
            {
                await Client.SendAsync(new ArraySegment<byte>(buf.Array, buf.ArrayOffset, buf.ReadableBytes), messageType, true, CancellationToken.None);
            }
            buf.Release();
        }

        /// <summary>
        /// 检查连接超时
        /// </summary>
        public virtual void CheckConnectorTimeout()
        {
            if (_Status.Status().Equals("Connecting"))
            {
                return;
            }
            var lTime = (DateTime.Now - _ConnectDate).TotalMilliseconds;
            try
            {
                if (lTime > _ConnectTimeoutMSEL)
                {
                    ConnectFail(new TimeoutException($"Connect {RemoteAddress()} Timeout"));
                }
                else
                {
                    UpdateActivityTime();
                }

            }
            catch
            {
                if (Client != null)
                {
                    Client.Dispose();
                }
                Client = null;
            }
        }



        private DateTime mErrorCommandTime = DateTime.MinValue;
        /// <summary>
        /// 检查连接状态
        /// </summary>
        public virtual void CheckConnectedStatus()
        {

            if (!CheckIsInvalid())
            {
                if (_CommandList.Count > 0)
                    CheckCommandList();
            }
        }

    }
}
